﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace CTC
{
    /// <summary>
    /// 主控端(卡头)
    /// </summary>
    public class CTCSlave : CTCDevice
    {

        #region 事件

        /// <summary>
        /// 连接状态变化函数
        /// </summary>
        public event CTCEvent.ClientConnStateEvent OnStateChange;
        /// <summary>
        /// 主设备状态改变
        /// </summary>
        public event CTCEvent.ByteDataEvent OnMasterStateChange;
        /// <summary>
        /// 抄表
        /// </summary>
        public event CTCEvent.EmptyEvent OnMeterRead;

        /// <summary>
        /// 卡头投币
        /// </summary>
        public event CTCEvent.ByteDataEvent OnCoinIn;

        /// <summary>
        /// 查询每局投币回应
        /// </summary>
        public event CTCEvent.EmptyEvent OnQueryCoin;

        /// <summary>
        /// 握手成功
        /// </summary>
        public event CTCEvent.ByteDataEvent OnHandshake;

        /// <summary>
        /// 设置每局投币
        /// </summary>
        public event CTCEvent.ByteDataEvent OnSetCoinPrice;
        /// <summary>
        /// 通道选择
        /// </summary>
        public event CTCEvent.ByteDataEvent OnSelectChannel;


        #endregion

        public CTCSlave(byte player, String comName, UInt32 prudoctID, byte[] deviceID) : base(player, comName, 38400)
        {
            SlaveDeviceId = deviceID;
            SlaveProductId = prudoctID;
            SlaveCTCversion = CTC_SLAVE_VERSION;

        }
        protected override void HandleOffline()
        {
            base.HandleOffline();
            OnStateChange?.Invoke(Player, DeviceConnState);
        }

        protected override void HandleReceive(DataPacket dataPacket)
        {
            if (CheckInst(dataPacket.Inst, CTCMsg.CTC_CMD_HANDSHAKE))
            {//握手应答
                if (dataPacket.DataLenght != CTCMsg.CTC_CMDLEN_HANDSHAKE)
                {
                    LogE(String.Format("CTC Handshake Rsp Length Error:{0}", dataPacket.DataLenght));
                    SendACK(dataPacket, CTCMsg.CTC_ACK_LENGTH_ERR);
                    return;
                }
                isHandshake = true;
                SendACK(dataPacket, CTCMsg.CTC_ACK_SUCCESS);
                DeviceConnState = CTCClientConnState.Connected;
                MasterCTCversion = CRC.BytesToUint16(dataPacket.Data[20], dataPacket.Data[21]);
                OnStateChange?.Invoke(Player, DeviceConnState);
                RspHandshake();
                OnHandshake?.Invoke(Player, dataPacket.Data);
                return;
            }
            //如果没有完成握手就不处理其它指令
            if (!isHandshake)
            {
                return;
            }
            if (CheckInst(dataPacket.Inst, CTCMsg.CTC_CMD_HEARTBEAT))
            {//心跳应答
                if (dataPacket.DataLenght != CTCMsg.CTC_CMDLEN_HEARTBEAT)
                {
                    LogE(String.Format("CTC Heartbeat Length Error:{0}", dataPacket.DataLenght));
                    SendACK(dataPacket, CTCMsg.CTC_ACK_LENGTH_ERR);
                    return;
                }
                SendACK(dataPacket, CTCMsg.CTC_ACK_SUCCESS);
                RspHeartbeat(SlaveState, SlaveErrorCode);
                byte masterState = dataPacket.Data[0];
                if (masterState != MasterState)
                {
                    MasterState = masterState;
                    OnMasterStateChange?.Invoke(Player, dataPacket.Data);
                }
            }

            else if (CheckInst(dataPacket.Inst, CTCMsg.CTC_CMD_METER_READ))
            {//读表数据
                if (dataPacket.DataLenght != 0)
                {
                    LogE(String.Format("CTC Meter Data Rsp Length Error:{0}", dataPacket.DataLenght));
                    SendACK(dataPacket, CTCMsg.CTC_ACK_LENGTH_ERR);
                    return;
                }
                SendACK(dataPacket, CTCMsg.CTC_ACK_SUCCESS);
                OnMeterRead?.Invoke(Player);
            }
            else if (CheckInst(dataPacket.Inst, CTCMsg.CTC_CMD_COIN_IN))
            {//投币应答
                if (dataPacket.DataLenght != CTCMsg.CTC_CMDLEN_COIN_IN)
                {
                    LogE(String.Format("CTC Coin In Rsp Length Error:{0}", dataPacket.DataLenght));
                    SendACK(dataPacket, CTCMsg.CTC_ACK_LENGTH_ERR);
                    return;
                }
                SendACK(dataPacket, CTCMsg.CTC_ACK_SUCCESS);
                OnCoinIn?.Invoke(Player, dataPacket.Data);
            }
            else if (CheckInst(dataPacket.Inst, CTCMsg.CTC_CMD_CHANNEL_SEL))
            {// 通道选择请求
                if (dataPacket.DataLenght != CTCMsg.CTC_CMDLEN_CHANNEL_SEL)
                {
                    LogE(String.Format("CTC Query Coin Rsp Length Error:{0}", dataPacket.DataLenght));
                    SendACK(dataPacket, CTCMsg.CTC_ACK_LENGTH_ERR);
                    return;
                }
                SendACK(dataPacket, CTCMsg.CTC_ACK_SUCCESS);
                OnSelectChannel?.Invoke(Player, dataPacket.Data);
            }
            else if (CheckInst(dataPacket.Inst, CTCMsg.CTC_CMD_QUERY_COIN))
            {// 查询单价请求
                if (dataPacket.DataLenght != 0)
                {
                    LogE(String.Format("CTC Game Reward Length Error:{0}", dataPacket.DataLenght));
                    SendACK(dataPacket, CTCMsg.CTC_ACK_LENGTH_ERR);
                    return;

                }
                SendACK(dataPacket, CTCMsg.CTC_ACK_SUCCESS);
                OnQueryCoin?.Invoke(Player);
            }
            else if (CheckInst(dataPacket.Inst, CTCMsg.CTC_CMD_SET_COIN))
            {// 设置单价请求
                if (dataPacket.DataLenght != CTCMsg.CTC_CMDLEN_SET_COIN)
                {
                    LogE(String.Format("CTC Set Coin Price Length Error:{0}", dataPacket.DataLenght));
                    SendACK(dataPacket, CTCMsg.CTC_ACK_LENGTH_ERR);
                    return;
                }
                SendACK(dataPacket, CTCMsg.CTC_ACK_SUCCESS);
                OnSetCoinPrice?.Invoke(Player, dataPacket.Data);
            }
            else
            {// 不支持的指令
                LogE(String.Format("CTC Received Unsupported Command ID:{0}", CRC.ToHexStringTrim(dataPacket.Inst)));
                SendACK(dataPacket, CTCMsg.CTC_ACK_UNSUPPORTED);
            }
        }

        /// <summary>
        /// 回应握手
        /// </summary>
        protected void RspHandshake()
        {
            LogI("回应握手");
            byte[] data = new byte[22];
            Array.Copy(CRC.Uint32ToBytes(SlaveProductId), 0, data, 0, 4);
            Array.Copy(SlaveDeviceId, 0, data, 4, 16);
            Array.Copy(CRC.Uint16ToBytes(CTC_SLAVE_VERSION), 0, data, 20, 2);
            Send(new DataPacket(Player, CTCMsg.CTC_CMD_HANDSHAKE_RSP, SendTransactionId++, data));
        }
        /// <summary>
        /// 是否可以消费
        /// </summary>
        /// <param name="isConsume">0=卡头可正常消费,1=卡头不可进行消费</param>
        /// <param name="status">0=无故障,非0代表有故障</param>
        protected void RspHeartbeat(byte isConsume, UInt16 status)
        {
            byte[] data = new byte[3];
            data[0] = isConsume;
            Array.Copy(CRC.Uint16ToBytes(status), 0, data, 1, 2);
            Send(new DataPacket(Player, CTCMsg.CTC_CMD_HEARTBEAT_RSP, SendTransactionId++, data));
        }
        /// <summary>
        /// 投币应答
        /// </summary>
        /// <param name="isAccept">0=成功接收,1=拒绝接收</param>
        /// <param name="orderID">投币时传递的交易ID</param>
        public void RspCoinIn(byte isAccept, byte[] orderID)
        {
            byte[] data = new byte[17];
            data[0] = isAccept;
            Array.Copy(orderID, 0, data, 1, 16);
            Send(new DataPacket(Player, CTCMsg.CTC_CMD_COIN_IN_RSP, SendTransactionId++, data));
        }
        /// <summary>
        /// 查询币每局多少币请求
        /// </summary>
        /// <param name="number"></param>
        public void RspQueryCoin(UInt16 number)
        {
            byte[] data = new byte[2];
            Array.Copy(CRC.Uint16ToBytes(number), 0, data, 0, 2);
            Send(new DataPacket(Player, CTCMsg.CTC_CMD_QUERY_COIN_RSP, SendTransactionId++, data));
        }
        /// <summary>
        /// 回应抄表指令
        /// </summary>
        /// <param name="isConsume"></param>
        /// <param name="status"></param>
        public void RspMeterRead(byte[] data)
        {
            Send(new DataPacket(Player, CTCMsg.CTC_CMD_METER_DATA, SendTransactionId++, data));
        }
        /// <summary>
        /// 实物奖励上报
        /// </summary>
        /// <param name="type">0=实物币,1=实物彩票,2=实体蓝票,3=实体卡片,100=实物礼品1,101=实物礼品2...130=实物礼品31</param>
        /// <param name="number">上报数量</param>
        public void SendPhysicalReport(byte type, UInt32 number)
        {
            byte[] data = new byte[5];
            data[0] = type;
            Array.Copy(CRC.Uint32ToBytes(number), 0, data, 1, 4);
            Send(new DataPacket(Player, CTCMsg.CTC_CMD_PHYSICAL_REPORT, SendTransactionId++, data));
        }
        /// <summary>
        /// 游戏有奖励给玩家(游戏机到卡头)
        /// </summary>
        /// <param name="type">0=奖励彩票,1=奖励蓝票,10=游戏任务得分</param>
        /// <param name="number">奖励的数量</param>
        public void SendReward(byte type, UInt32 number)
        {
            byte[] data = new byte[5];
            data[0] = type;
            Array.Copy(CRC.Uint32ToBytes(number), 0, data, 1, 4);
            Send(new DataPacket(Player, CTCMsg.CTC_CMD_REWARD, SendTransactionId++, data));
        }
        /// <summary>
        /// 游戏氛围事件(游戏机到卡头)
        /// </summary>
        /// <param name="level">0开始,数字越大氛围越强烈</param>
        /// <param name="type">0=奖励彩票,1=奖励蓝票,10=游戏任务得分,100=奖励实物</param>
        /// <param name="number">产生的奖励相关的数量</param>
        public void SendAtmosphere(UInt16 level, byte type, UInt32 number)
        {
            byte[] data = new byte[7];
            Array.Copy(CRC.Uint16ToBytes(level), 0, data, 0, 2);
            data[2] = type;
            Array.Copy(CRC.Uint32ToBytes(number), 0, data, 3, 4);
            Send(new DataPacket(Player, CTCMsg.CTC_CMD_ATMOSPHEREEVENT, SendTransactionId++, data));
        }

        /// <summary>
        /// 变更状态
        /// </summary>
        /// <param name="state">0=卡头可正常消费,1=卡头不可进行消费</param>
        /// <param name="errorCode">0=无故障,非0代表有故障</param>
        public void ChangeState(byte state, UInt16 errorCode)
        {
            SlaveState = state;
            SlaveErrorCode = errorCode;
            SendHeartbeat();
        }

    }
}
